<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">

<HTML>
   <HEAD>
      <TITLE>Developing Applications with the DRMAA Java<SUP>TM</SUP> Language Binding</TITLE>
   </HEAD>
   <BODY>
   <H1>
      <FONT COLOR="#336699">
         Distributed Resource Management Application API
      </FONT>
   </H1>

   <P STYLE="margin-bottom: 0cm">
      This guide is a tutorial for getting started programming with the DRMAA
      Java<sup>TM</SUP>sup> language binding.  It assumes that you already know
      what DRMAA is and know how to find and install the Grid Engine 6.0u2
      release.  If you do not already know these things, try these web sites:
   </P>

   <UL>
      <LI>
         <A HREF="http://www.drmaa.org">
            The DRMAA Website
         </A>
      </LI>
      <LI>
         <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/%7Echeckout%7E/gridengine/doc/javadocs/index.html?content-type=text/html">
            The Grid Engine 6.0 DRMAA Java language binding JavaDocs
         </A>
      </LI>
      <LI>
         <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/%7Echeckout%7E/gridengine/doc/README-DRMAA-Java.txt?content-type=text/plain">
            The Grid Engine 6.0 DRMAA Java language binding README
         </A>
      </LI>
      <LI>
         <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/%7Echeckout%7E/gridengine/doc/README-DRMAA.txt?content-type=text/plain">
            The Grid Engine 6.0 DRMAA C binding README
         </A>
      </LI>
      <LI>
         <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/%7Echeckout%7E/gridengine/source/libs/japi/drmaa.html?content-type=text/html">
            The Grid Engine libdrmaa docs
         </A>
      </LI>
      <LI>
         <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/%7Echeckout%7E/gridengine/source/libs/japi/japi.html?content-type=text/html">
            The Grid Engine libjapi docs
         </A>
      </LI>
   </UL>

   <P STYLE="margin-bottom: 0cm">
      Note that the example programs in this howto can be found in the CVS
      <A HREF="http://gridengine.sunsource.net/source/browse/gridengine/source/classes/com/sun/grid/drmaa/howto/">source tree</A>.
   </P>

   <H2>
      <FONT COLOR="#336699">
         Starting and Stopping a Session
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      The following code segment shows the most basic DRMAA Java language
      binding program:
   </P>

   <H3>Example 1</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06:
07: public class Howto1 {
08:    public static void main(String[] args) {
09:       SessionFactory factory = SessionFactory.getFactory();
10:       Session session = factory.getSession();
11:
12:       try {
13:          session.init(&quot;&quot;);
14:          session.exit();
15:       } catch (DrmaaException e) {
16:          System.out.println("Error: " + e.getMessage());
17:       }
18:    }
19: }</PRE>

   <P STYLE="margin-bottom: 0cm">
      Everything that you as a programmer will do with DRMAA, you will do
      through a Session object.  You will get the Session object from
      a SessionFactory.  You will get the SessionFactory from the
      static SessionFactory.getFactory() method.  The reason for this
      chain is that the org.ggf.drmaa.* classes should be considered an
      immutable package to be used by every DRMAA Java language binding
      implementation.  Because the package is immutable, to load a specific
      implementation, the SessionFactory uses a system property to find
      the implementation's session factory, which it then loads.  That session
      factory is then responsible for creating the session in whatever why it
      sees fit.  Without the session factory, the Session would have to
      do that work, and the implementation-defined session object would have to
      be instantiated with some predefined constructor signature.  With the
      session factory, the implementation is free to define for itself how the
      session object will be instatiated.  It should be noted that even though
      there is a session factory, only one session may exist at a time.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Now let's look at the code.  First, on line 9, we get a session factory
      instance with SessionFactory.getFactory(), and on line 10 we get the
      session instance with SessionFactory.getSession().  Once we have the
      session, we can initialize it on line 13 by calling Session.init().
      We pass in <CODE>&quot;&quot;</CODE> as the contact string because we're
      creating a new session, and the C binding upon which the Java language
      binding is built does not recognize any initialization arguments.  (See
      Example 1.1.)  The plan is that at some point in the future the contact
      string will be a combination of $SGE_ROOT and $SGE_CELL.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Session.init() creates a session and starts an event client listener
      thread.  The session is used for organizing jobs submitted through DRMAA,
      and the thread is used to receive updates from the queue master about the
      state of jobs and the system in general.  Once Session.init() has
      been called successfully, it is the responsibility of the calling
      application to also call Session.exit() before terminating.  If an
      application does not call Session.exit() before terminating, session
      state may be left behind in the user's home directory (under .sge/drmaa),
      and the queue master may be left with a dead event client handle, which
      can decrease queue master performance.  I recommend using the
      Runtime.addShutdownHook () method to make sure Session.exit() gets
      called.
   </P>

   <P STYLE="margin-bottom: 0cm">
      At the end of our program, on line 14, we call Session.exit().
      Session.exit() cleans up the session and stops the event client
      listener thread.  Most other DRMAA methods must be called before
      Session.exit().  Some functions, like Session.getContact(), can
      be called after exit(), but these functions only provide general
      information.  Any function that does work, such as Session.runJob()
      or Session.wait() must be called before Session.exit() is
      called.  If such a function is called after exit() is called, it will
      throw a NoActiveSessionException.
   </P>

   <H3>Example 1.1</H3>
   
<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06:
07: public class Howto1_1 {
08:    public static void main(String[] args) {
09:       SessionFactory factory = SessionFactory.getFactory();
10:       Session session = factory.getSession();
11:
12:       try {
13:          session.init(&quot;&quot;);
14:          String contact = session.getContact();
15:          session.exit();
16:
17:          session.init(contact);
18:          session.exit();
19:       } catch (DrmaaException e) {
20:          System.out.println("Error: " + e.getMessage());
21:       }
22:    }
23: }</PRE>

   <p style="margin-bottom: 0cm">
      This example is very similar to Example 1.  The difference is that it uses
      the Grid Engine feature of reconnectable sessions.  The DRMAA concept of
      a session is translated into a session tag in the Grid Engine job
      structure.  That means that every job knows to which session it belongs.
      With reconnectable sessions, it's possible to initialize the DRMAA library
      to a previous session, allowing the library access to that session's job
      list.  The only limitation, though, is that jobs which end between the
      calls to exit() and init() will be lost, as the reconnecting session will
      no longer see these jobs, and so won't know about them.
   </p>

   <p style="margin-bottom: 0cm">
      Through line 13, this example is identical to Example 1.  On line 14,
      however, we use the getContact() method to get the contact information
      for this session.  On line 15 we then exit the session.  On line 17, we
      use the stored contact information to reconnect to the previous session.
      Had we submitted jobs before calling exit(), those jobs would now be
      available again for operations such as wait() and synchronize().  Finally,
      on line 18 we exit the session a second time.
   </p>

   <H2>
      <FONT COLOR="#336699">
         Running a Job
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      The following code segment shows how to use the DRMAA Java language
      binding to submit a job to Grid Engine:
   </P>

   <H3>Example 2</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto2 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init(&quot;&quot;);
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          session.deleteJobTemplate(jt);
25:          session.exit();
26:       } catch (DrmaaException e) {
27:          System.out.println("Error: " + e.getMessage());
28:       }
29:    }
30: }</PRE>



   <P STYLE="margin-bottom: 0cm">
      The beginning and end of this program are the same as the previous one.
      What's different is in lines 16-24.  On line 16 we ask DRMAA to allocate a
      JobTemplate for us.  A JobTemplate is an Object used to store
      information about a job to be submitted.  The same template can be reused
      for multiple calls to Session.runJob() or Session.runBulkJobs().
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 17 we set the remoteCommand attribute.  This
      attribute tells DRMAA where to find the program we want to run.  Its value
      is the path to the executable.  The path be be either relative or
      absolute.  If relative, it is relative to the workingDirectory
      attribute, which if not set defaults to the user's home directory.  For
      more information on DRMAA attributes, please see the
      <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/~checkout~/gridengine/doc/htmlman/htmlman3/drmaa_attributes.html">drmaa_attributes</A>
      man page.  Note that for this program to work, the script
      &quot;sleeper.sh&quot; must be in your default path, i.e. the path set by
      your shell script when you log in.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 18 we set the args attribute.  This
      attribute tells DRMAA what arguments to pass to the executable.  For
      more information on DRMAA attributes, please see the
      <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/~checkout~/gridengine/doc/htmlman/htmlman3/drmaa_attributes.html">drmaa_attributes</A>
      man page.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 20 we submit the job with Session.runJob().  This method will
      return the id assigned to the job by the queue master.  The job is now
      running as though submitted by qsub.  At this point calling
      Session.exit() and/or terminating the program will have no effect on
      the job.
   </P>

   <P STYLE="margin-bottom: 0cm">
      To clean things up, we delete the job template on line 24.  This frees the
      memory DRMAA set aside for the job template, but has no effect on
      submitted jobs.
   </P>

   <P STYLE="margin-bottom: 0cm">
      If instead of a single job we had wanted to submit an array job, we could
      have replaced the code on lines 20-22 with the following:
   </P>
   
   <H3>Example 2.1</H3>

<PRE>20: java.util.List ids = session.runBulkJobs(jt, 1, 30, 2);
21: java.util.Iterator i = ids.iterator();
22:
23: while (i.hasNext()) {
24:    System.out.println("Your job has been submitted with id " + i.next());
25: }</PRE>
   
   <P STYLE="margin-bottom: 0cm">
      This code segment submits an array job with 15 tasks numbered 1, 3, 5, 7,
      etc.  An important difference to note is that Session.runBulkJobs()
      returns the job ids in a java.util.List.  On line 21 we get an Iterator
      for the list and loop through, printing each submitted job's id, on lines
      23-25.
   </P>
   
   <H2>
      <FONT COLOR="#336699">
         Waiting for a Job
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      Now we're going to extend our example to include waiting for a job to
      finish.
   </P>
   
   <H3>Example 3</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import java.util.Iterator;
05: import java.util.Map;
06: import org.ggf.drmaa.DrmaaException;
07: import org.ggf.drmaa.JobInfo;
08: import org.ggf.drmaa.JobTemplate;
09: import org.ggf.drmaa.Session;
10: import org.ggf.drmaa.SessionFactory;
11:
12: public class Howto3 {
13:    public static void main(String[] args) {
14:       SessionFactory factory = SessionFactory.getFactory();
15:       Session session = factory.getSession();
16:
17:       try {
18:          session.init("");
19:          JobTemplate jt = session.createJobTemplate();
20:          jt.setRemoteCommand("sleeper.sh");
21:          jt.setArgs(Collections.singletonList("5"));
22:
23:          String id = session.runJob(jt);
24:
25:          System.out.println("Your job has been submitted with id " + id);
26:
27:          session.deleteJobTemplate(jt);
28:
29:          JobInfo info = session.wait(id, Session.TIMEOUT_WAIT_FOREVER);
30:
31:          if (info.wasAborted()) {
32:             System.out.println("Job " + info.getJobId() + " never ran");
33:          } else if (info.hasExited()) {
34:             System.out.println("Job " + info.getJobId() +
35:                   " finished regularly with exit status " +
36:                   info.getExitStatus());
37:          } else if (info.hasSignaled()) {
38:             System.out.println("Job " + info.getJobId() +
39:                   " finished due to signal " +
40:                   info.getTerminatingSignal());
41:          } else {
42:             System.out.println("Job " + info.getJobId() +
43:                   " finished with unclear conditions");
44:          }
45:
46:          System.out.println("Job Usage:");
47:
48:          Map rmap = info.getResourceUsage();
49:          Iterator i = rmap.keySet().iterator();
50:
51:          while (i.hasNext()) {
52:             String name = (String)i.next();
53:             String value = (String)rmap.get(name);
54:
55:             System.out.println("  " + name + "=" + value);
56:          }
57:
58:          session.exit();
59:       } catch (DrmaaException e) {
60:          System.out.println("Error: " + e.getMessage());
61:       }
62:    }
63: }</PRE>


   <P STYLE="margin-bottom: 0cm">
      This example is very similar to Example 2 except for lines 29-56.  (The
      line numbers are shifted from Example 2 because of the additional import
      statements)  On line 29 we call Session.wait() to
      wait for the job to end.  We have to give Session.wait() both the id
      of the job for which we want to wait and how long we are willing to wait
      for the job to finish.  The later could be a number of seconds, or it
      could be either <CODE>Session.DRMAA_TIMEOUT_WAIT_FOREVER</CODE> or
      <CODE>Session.DRMAA_TIMEOUT_NO_WAIT</CODE>.  Session.wait()
      returns a JobInfo object, which contains useful information how the job
      exited and how much resources it used.  The JobInfo object also contains
      the id of the job for which we actually waited because the job id we pass
      in could be <CODE>Session.DRMAA_JOB_IDS_SESSION_ANY</CODE>, in which
      case Session.wait() must have a way of tell us which job is the one
      that made it return.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Assuming the wait worked, we query the job's exit status on lines 31-44
      using the JobInfo accessor methods.  This if structure is a common usage
      pattern for Session.wait() and should be encapsulated in a method for
      ease of use.
   </P>

   <P STYLE="margin-bottom: 0cm">
      After checking the exit status, we query the job's usage on lines 46-56.
      We use JobInfo.getResourceUsage() to get a Map of the resources consumed
      and print out the results.
   </P>

   <P STYLE="margin-bottom: 0cm">
      An alternative to Session.wait() when working with multiple jobs,
      such as jobs submitted by Session.runBulkJobs() or multiple calls to
      Session.runJob() is Session.synchronize().
      Session.synchronize() waits for a List of jobs to finish.  To use
      Session.synchronize(), we could replace lines 23-56 with the
      following:
   </P>

   <H3>Example 3.1</H3>

<PRE>23: List ids = session.runBulkJobs(jt, 1, 30, 2);
24: Iterator i = ids.iterator();
25:
26: while (i.hasNext()) {
27:    System.out.println("Your job has been submitted with id " + i.next());
28: }
29:
30: session.deleteJobTemplate(jt);
31: session.synchronize(Collections.singletonList(Session.JOB_IDS_SESSION_ALL),
32:       Session.TIMEOUT_WAIT_FOREVER, true);
33:
34: System.out.println("All jobs have finished.");</PRE>

   <H3>Example 3.1</H3>

   <P STYLE="margin-bottom: 0cm">
      Lines 23-28 now call Session.runBulkJobs() so that we have several
      jobs for which to wait.  On line 31, instead of calling
      Session.wait(), we call Session.synchronize().
      Session.synchronize() takes only three parameters.  The first is the
      List of ids for which to wait.  If the special id,
      <CODE>Session.DRMAA_JOB_IDS_SESSION_ALL</CODE>, appears in the List,
      Session.synchronize() will wait for all jobs submitted via DRMAA
      during this session, i.e. since Session.init() was called.  The
      second parameter is how long to wait for all the jobs in the list to
      finish.  This is the same as the timeout parameter for
      Session.wait().  The third parameter is whether this call to
      Session.synchronize() should clean up after the job.  After a job
      completes, it leaves behind accounting information, such as exist status
      and usage, until either Session.wait() or Session.synchronize()
      with dispose set to <CODE>true</CODE> is called.  It is the responsibility
      of the application to make sure one of these two functions is called for
      every job.  Not doing so creates a memory leak.  Note that calling
      Session.synchronize() with dispose set to <CODE>true</CODE> flushes
      all accounting information for all jobs in the list.  If you want to use
      Session.synchronize() and still recover the accounting information,
      set dispose to <CODE>false</CODE> and call Session.wait() for each
      job.  To do this in Example 3, we would replace lines 23-56 with the
      following:
   </P>

   <H3>Example 3.2</H3>

<PRE>23: int start = 1;
24: int end  = 30;
25: int step = 2;
26:
27: List ids = session.runBulkJobs(jt, start, end, step);
28: Iterator i = ids.iterator();
29:
30: while (i.hasNext()) {
31:    System.out.println("Your job has been submitted with id " + i.next());
32: }
33:
34: session.deleteJobTemplate(jt);
35: session.synchronize(Collections.singletonList(Session.JOB_IDS_SESSION_ALL),
36:       Session.TIMEOUT_WAIT_FOREVER, false);
37:
38: for (int count = start; count < end; count += step) {
39:    JobInfo info = session.wait(Session.JOB_IDS_SESSION_ANY,
40:       Session.TIMEOUT_WAIT_FOREVER);
41:
42:    if (info.wasAborted()) {
43:       System.out.println("Job " + info.getJobId() + " never ran");
44:    } else if (info.hasExited()) {
45:       System.out.println("Job " + info.getJobId() +
46:             " finished regularly with exit status " +
47:             info.getExitStatus());
48:    } else if (info.hasSignaled()) {
49:       System.out.println("Job " + info.getJobId() +
50:             " finished due to signal " +
51:             info.getTerminatingSignal());
52:    } else {
53:       System.out.println("Job " + info.getJobId() +
54:             " finished with unclear conditions");
55:    }
56:
57:    System.out.println("Job Usage:");
58:
59:    Map rmap = info.getResourceUsage();
60:    Iterator r = rmap.keySet().iterator();
61:
62:    while (r.hasNext()) {
63:       String name = (String)r.next();
64:       String value = (String)rmap.get(name);
65:
66:       System.out.println("  " + name + "=" + value);
67:    }
68: }</PRE>

   <P STYLE="margin-bottom: 0cm">
      What's different is that on line 35, we set dispose to false, and then on
      lines 38-68 we wait once for each job, printing the exit status and
      usage information as we did in Example 3.  We pass
      <CODE>Session.DRMAA_JOB_IDS_SESSION_ANY</CODE> to Session.wait()
      as the job id because we already know that all the jobs have finished, so
      we don't really care in what order we process them.  In an interactive
      system where we couldn't guarantee that more jobs wouldn't be submitted
      between the synchronize and the wait, we would have to store the job ids
      from the Session.runBulkJobs() in an array and then wait for each job
      specifically.  Otherwise, the Session.wait() could end up waiting for
      a job submitted after the call to Session.synchronize().
   </P>

   <H2>
      <FONT COLOR="#336699">
         Controling a Job
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      Now let's look at an example of how to control a job from DRMAA:
   </P>

   <H3>Example 4</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto4 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init("");
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          session.control(id, Session.TERMINATE);
25:
26:          System.out.println("Your job has been deleted");
27:
28:          session.deleteJobTemplate(jt);
29:          session.exit();
30:       } catch (DrmaaException e) {
31:          System.out.println("Error: " + e.getMessage());
32:       }
33:    }
34: }</PRE>

   <P STYLE="margin-bottom: 0cm">
      This example is very similar to Example 2 except for lines 24-26.  On line
      20 we use Session.control() to delete the job we just submitted.
      Aside from deleting the job, we could have also used
      Session.control() to suspend, resume, hold, or release it.  For more
      information, see the
      <A HREF="http://gridengine.sunsource.net/unbranded-source/browse/~checkout~/gridengine/doc/htmlman/htmlman3/drmaa_control.html">drmaa_control</A>
      man page.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Note that Session.control() can be used to control jobs not submitted
      through DRMAA.  Any valid SGE job id could be passed to
      Session.control() as the id of the job to delete.
   </P>

   <H2>
      <FONT COLOR="#336699">
         Getting Job Status
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      Here's an example of using DRMAA to query the status of a job:
   </P>

   <H3>Example 5</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import java.util.Collections;
04: import org.ggf.drmaa.DrmaaException;
05: import org.ggf.drmaa.JobTemplate;
06: import org.ggf.drmaa.Session;
07: import org.ggf.drmaa.SessionFactory;
08:
09: public class Howto5 {
10:    public static void main(String[] args) {
11:       SessionFactory factory = SessionFactory.getFactory();
12:       Session session = factory.getSession();
13:
14:       try {
15:          session.init("");
16:          JobTemplate jt = session.createJobTemplate();
17:          jt.setRemoteCommand("sleeper.sh");
18:          jt.setArgs(Collections.singletonList("5"));
19:
20:          String id = session.runJob(jt);
21:
22:          System.out.println("Your job has been submitted with id " + id);
23:
24:          try {
25:             Thread.sleep(20 * 1000);
26:          } catch (InterruptedException e) {
27:             // Don't care
28:          }
29:
30:          int status = session.getJobProgramStatus(id);
31:
32:          switch (status) {
33:             case Session.UNDETERMINED:
34:                System.out.println("Job status cannot be determined\n");
35:                break;
36:             case Session.QUEUED_ACTIVE:
37:                System.out.println("Job is queued and active\n");
38:                break;
39:             case Session.SYSTEM_ON_HOLD:
40:                System.out.println("Job is queued and in system hold\n");
41:                break;
42:             case Session.USER_ON_HOLD:
43:                System.out.println("Job is queued and in user hold\n");
44:                break;
45:             case Session.USER_SYSTEM_ON_HOLD:
46:                System.out.println("Job is queued and in user and system hold\n");
47:                break;
48:             case Session.RUNNING:
49:                System.out.println("Job is running\n");
50:                break;
51:             case Session.SYSTEM_SUSPENDED:
52:                System.out.println("Job is system suspended\n");
53:                break;
54:             case Session.USER_SUSPENDED:
55:                System.out.println("Job is user suspended\n");
56:                break;
57:             case Session.USER_SYSTEM_SUSPENDED:
58:                System.out.println("Job is user and system suspended\n");
59:                break;
60:             case Session.DONE:
61:                System.out.println("Job finished normally\n");
62:                break;
63:             case Session.FAILED:
64:                System.out.println("Job finished, but failed\n");
65:                break;
66:          } /* switch */
67:
68:          session.deleteJobTemplate(jt);
69:          session.exit();
70:       } catch (DrmaaException e) {
71:          System.out.println("Error: " + e.getMessage());
72:       }
73:    }
74: }</PRE>


   <P STYLE="margin-bottom: 0cm">
      Again, this example is very similar to Example 2, this time with the
      exception of lines 24-68.  First, after submitting the job, we sleep for
      20 seconds to give SGE time to schedule the job.  Then, on line 30, we
      use Session.getJobProgramStatus() to get the status of the job.
      Lines 32-66 determine what the job status is and report it.  This switch
      statement is a common usage pattern for Session.getJobProgramStatus() and
      should be encapsulated in a method for ease of use.
   </P>

   <H2>
      <FONT COLOR="#336699">
         Getting DRM Information
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      Next, let's look at how to query the DRMAA library for information about
      the DRMS and the DRMAA implementation itself:
   </P>

   <H3>Example 6</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.DrmaaException;
04: import org.ggf.drmaa.Session;
05: import org.ggf.drmaa.SessionFactory;
06: import org.ggf.drmaa.Version;
07:
08: public class Howto6 {
09:    public static void main(String[] args) {
10:       SessionFactory factory = SessionFactory.getFactory();
11:       Session session = factory.getSession();
12:
13:       try {
14:          System.out.println("Supported contact strings: \"" +
15:                session.getContact() + "\"");
16:          System.out.println("Supported DRM systems: \"" +
17:                session.getDrmSystem() + "\"");
18:          System.out.println("Supported DRMAA implementations: \"" +
19:                session.getDrmaaImplementation() + "\"");
20:
21:          session.init("");
22:
23:          System.out.println("Using contact strings: \"" +
24:                session.getContact() + "\"");
25:          System.out.println("Using DRM systems: \"" +
26:                session.getDrmSystem() + "\"");
27:          System.out.println("Using DRMAA implementations: \"" +
28:                session.getDrmaaImplementation() + "\"");
29:
30:          Version version = session.getVersion();
31:
32:          System.out.println("Using DRMAA version " + version.toString());
33:
34:          session.exit();
35:       } catch (DrmaaException e) {
36:          System.out.println("Error: " + e.getMessage());
37:       }
38:    }
39: }</PRE>

   <P STYLE="margin-bottom: 0cm">
      On line 14, we print the contact string list.  This is the list of contact
      strings that will be understood by this DRMAA instance.  Normally one of
      these strings is used to select to which DRM this DRMAA instance should
      be bound.  In the Grid Engine 6.0u2 implementation, the contact string list
      is empty because there is only ever one possible DRM to which to bind.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 16, we print the list of supported DRM systems.  For the Grid
      Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 18, we print the list of supported DRMAA implementations.  For the
      Grid Engine 6.0u2 implementation, this will always be Grid Engine 6.0u2.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 21, we call Session.init().  After Session.init() has
      been called, the Session.getContact(), Session.getDrmSystem(),
      and Session.getDrmaaImplementation() calls change.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 23, we call Session.getContact() again, this time to get the
      contact string that was used to bind to a DRMS in Session.init(). For
      the Grid Engine 6.0u2 implementation, this will always be an empty string.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 25, we call Session.getDrmSystem() again, this time to get
      the name of the DRMS to which DRMAA is bound.  For the Grid Engine 6.0u2
      implementation, this will always be Grid Engine 6.0u2.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 27, we call Session.getDrmaaImplementation() again, this time
      to get the name of the DRMAA implementation to which the application is
      bound.  For the Grid Engine 6.0u2 implementation, this will always be Grid
      Engine 6.0u2.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 30, we get the version number of the DRMAA Java language binding
      specification supported by this DRMAA implementation.  For the Grid Engine
      6.0u2 implementation this is currently version 0.5.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Finally, on line 34, we end the session with drmaa_exit().
   </P>

   <H2>
      <FONT COLOR="#336699">
         Using the New Utility Classes
      </FONT>
   </H2>

   <P STYLE="margin-bottom: 0cm">
      The JobTemplate class uses two new classes to represent some of its
      property types.  They are FileTransferMode and PartialTimestamp.  The
      PartialTimestampFormat is used to print a PartialTimestamp.  The Session
      interface uses the Version class to represent version information.
   </P>

   <H3>FileTransferMode</H3>

   <P STYLE="margin-bottom: 0cm">
      The FileTransferMode class is a simple container which holds the state of
      three boolean properties: errorStream, inputStream, and outputStream.
      Each property indicated whether the given stream should be used for file
      transfers.  When a given property is true, the corresponding path
      property is taken to represent the source or destination for the file
      transfer.  For example, if the errorStream property of the active
      FileTransferMode object is true, the errorPath property of the JobTemplate
      object is treated as the destination to which to copy the resulting error
      output.
   </P>

   <H3>PartialTimestamp</H3>

   <P STYLE="margin-bottom: 0cm">
      The PartialTimestamp class is used to represent incomplete time
      specifications, e.g. &quot;1:00&quot; as opposed to &quot;2005/01/05
      01:00&quot;.  Such partial time specifications will be resolved to the
      soonest time which is still in the future.  For example, before 1:00,
      &quot;1:00&quot; will be resolved to 1:00 on that same day.  After 1:00,
      it will be resolved to 1:00 on the next day.  The PartialTimestamp class
      is a subclass of java.util.Calendar and behaves as much like
      java.util.GregorianCalendar as possible, with the exception of allowing
      incomplete time specifications.
   </P>

   <P STYLE="margin-bottom: 0cm">
      The PartialTimestamp class has one other new feature not present in the
      java.util.Calendar class, field modifiers.  Since it may be desirable to
      appy a modifier to a field which is not yet set, the PartialTimestamp
      class allows for field modifiers to be kept separately from field values.
      If a modifier is applied to a set field, that modifier is ultimately added
      to the field's value.  If a modifier is applied to an unset field, the
      modifier is added to the field's value at the time it is resolved.  For
      example, if a PartialTimestamp object has HOUR_OF_DAY=1 and MINUTE=0 and
      a +1 modifier to the DAY_OF_MONTH field, and the PartialTimestamp object
      is resolved at 0:55 on a Tuesday, the result will be 1:00 on the following
      Wednesday.  If the same PartialTimestamp object were resolved at 1:01 on
      a Tuesday, the result would be 1:00 on the following Thursday.
   </P>

   <P STYLE="margin-bottom: 0cm">
      For a PartialTimestamp object, there are primary fields and secondary
      fields.  The primary fields are actually used by DRMAA and the
      PartialTimestampFormat class.  The secondary fields are used only to
      calculate the unset primary fields.  The primary fields are CENTURY, YEAR,
      MONTH, DAY_OF_MONTH/DATE, HOUR_OF_DAY, MINUTE, SECOND, and ZONE_OFFSET.
      The secondary fields are DAY_OF_WEEK, DAY_OF_YEAR, DAY_OF_WEEK_IN_MONTH,
      WEEK_OF_MONTH, and WEEK_OF_YEAR.  The MILLISECOND field is neither primary
      nor secondary because it outside of the scope of the DRMAA time stamp
      format specification, and hence is largely ignored.
   </P>

   <H3>PartialTimestampFormat</H3>

   <P STYLE="margin-bottom: 0cm">
      To convert a PartialTimestamp object to or from a String, one should use
      a PartialTimestampFormat object.  The parse() method converts a String
      into a PartialTimestamp object, and the format() method convers a
      PartialTimestamp object into a String.  In order for the
      PartialTimestampFormat class to properly do the conversions, the no field
      of a lower order than the highest order set field may be unset.  For
      example, if the DAY_OF_WEEK field is set, the HOUR_OF_DAY and MINUTE
      fields must also be set.  (These two field are actually required in all
      cases.)  If the MONTH field is set, the DAY_OF_MONTH, HOUR_OF_DAY, and
      MINUTE fields must be set.  If the CENTURY field is set, the YEAR, MONTH,
      DAY_OF_MONTH, HOUR_OF_DAY, and MINUTE fields must be set.  If the
      MILLISECOND field is set, the HOUR_OF_DAY, MINUTE, and SECOND fields must
      be set.  The SECOND and ZONE_OFFSET fields are always optional.  The same
      applies for strings to be parsed.
   </P>

   <P STYLE="margin-bottom: 0cm">
      Since the PartialTimestamp class is somewhat complicated, let's look at an
      example:
   </P>

   <H3>Example 7</H3>

<PRE>01: package com.sun.grid.drmaa.howto;
02:
03: import org.ggf.drmaa.PartialTimestamp;
04: import org.ggf.drmaa.PartialTimestampFormat;
05:
06: public class Howto7 {
07:    public static void main(String[] args) {
08:       try {
09:          PartialTimestamp date = new PartialTimestamp();
10:          PartialTimestampFormat format = new PartialTimestampFormat();
11:
12:          date.set(PartialTimestamp.HOUR_OF_DAY, 13);
13:          date.set(PartialTimestamp.MINUTE, 0);
14:
15:          System.out.println(format.format(date));
16:
17:          date.setModifier(PartialTimestamp.DAY_OF_MONTH, 1);
18:
19:          System.out.println(format.format(date));
20:          System.out.println(date.getTime().toString());
21:
22:          date.setTimeInMillis(System.currentTimeMillis());
23:
24:          System.out.println(format.format(date));
25:
26:          date = format.parse("01/05 14:07:29");
27:
28:          System.out.println(format.format(date));
29:       } catch (java.text.ParseException e) {
30:          System.out.println("Error: " + e.getMessage());
31:       }
32:    }
33: }</PRE>

   <P STYLE="margin-bottom: 0cm">
      Since the PartialTimestamp class is useable outside of a DRMAA session,
      this example does not create a SessionFactory or Session instance.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 9, we create a PartialTimestamp with no fields set.  Other
      constructors could be used to create a PartialTimestamp object with
      various fields already set.  See the JavaDocs for more details.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 10, we create a PartialTimestampFormat object.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On lines 12 and 13, we set the two required PartialTimestamp fields.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 15, we use the PartialTimestampFormat object to convert the
      PartialTimestamp to a String and print it out.  This will print
      &quot;13:00&quot;.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 17, we set a modifier for the DAY_OF_MONTH field.  This means that
      when the PartialTimestamp is resolved to a concrete time, that time will
      be one day later than the earliest time which is still in the future.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 19, we use the PartialTimestampFormat object to convert the
      PartialTimestamp to a String and print it out again.  This will also print
      &quot;13:00&quot; because modifiers are ignored by the
      PartialTimestampFormat class.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 20, we resolve the PartialTimestamp to a concrete Date object and
      print the value of that object.  If this code is executed at 3:49:25pm on
      Tuesday, January 4th 2005, the result will be &quot;Thu Jan 06 13:00:00
      MET 2005&quot;.  (The timezone is MET because that's where I am.)
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 22, we set the value of the time to the current time in
      milliseconds.  Setting the time directly, either in milliseconds or as a
      Date object, results in all fields being set.
   </P>

   <P STYLE="margin-bottom: 0cm">
      On line 24, we use the PartialTimestampFormat object to convert the
      PartialTimestamp to a String and print it out.  Because all the fields
      have been set, the result is &quot;2005/01/05 15:49:25 +01:00&quot;.
      (Again, the timezone setting is because I am in GMT+1.)
   </P>

   <P STYLE="margin-bottom: 0cm">
      On lines 26-28, we use the PartialTimestampFormat object to convert the
      String to a PartialTimestamp and then back to a String to be printed it
      out.  The result is the same string, &quot;01/05 14:07:29&quot;
   </P>

   <H3>Version</H3>

   <P STYLE="margin-bottom: 0cm">
      The Version class is used by the Session instance to return the version of
      DRMAA supported by the implementation.  The major and minor fields of the
      Version class represent the major and minor version numbers, respectively.
      For example, DRMAA 1.0 is represented by a Version object with major=1 and
      minor=0.
   </P>
   </BODY>
</HTML>
